//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __UART_H__
#define __UART_H__

typedef UInt16 IO_PORT;

typedef enum SerialBaudRate
{
    SerialBaudRate_110          = 110,
    SerialBaudRate_300          = 300,
    SerialBaudRate_600          = 600,
    SerialBaudRate_1200         = 1200,
    SerialBaudRate_2400         = 2400,
    SerialBaudRate_4800         = 4800,
    SerialBaudRate_9600         = 9600,
    SerialBaudRate_14400        = 14400,
    SerialBaudRate_19200        = 19200,
    SerialBaudRate_38400        = 38400,
    SerialBaudRate_56000        = 56000,
    SerialBaudRate_57600        = 57600,
    SerialBaudRate_115200       = 115200,
    SerialBaudRate_128000       = 128000,
    SerialBaudRate_256000       = 256000,

    SerialBaudRate_Default      = SerialBaudRate_9600,
} SerialBaudRate;

typedef enum SerialParity
{
    SerialParity_No             = 0,
    SerialParity_Odd            = 1,
    SerialParity_Even           = 2,
    SerialParity_Mark           = 3,
    SerialParity_Space          = 4,

    SerialParity_Default        = SerialParity_No,
} SerialParity;

typedef enum SerialStopBits
{
    SerialStopBits_One          = 0,
    SerialStopBits_One5         = 1,
    SerialStopBits_Two          = 2,

    SerialStopBits_Default      = SerialStopBits_One,
} SerialStopBits;

typedef enum SerialByteSize
{
    SerialByteSize_5            = 5,
    SerialByteSize_6            = 6,
    SerialByteSize_7            = 7,
    SerialByteSize_8            = 8,

    SerialByteSize_Default      = SerialByteSize_8,
} SerialByteSize;

/* UART registers' address */
#define _TXB(base)          ((IO_PORT)((base) + 0))
#define _RXB(base)          ((IO_PORT)((base) + 0))
#define _DLLB(base)         ((IO_PORT)((base) + 0))
#define _IER(base)          ((IO_PORT)((base) + 1))
#define _DLHB(base)         ((IO_PORT)((base) + 1))
#define _IIR(base)          ((IO_PORT)((base) + 2))
#define _FCR(base)          ((IO_PORT)((base) + 2))
#define _LCR(base)          ((IO_PORT)((base) + 3))
#define _MCR(base)          ((IO_PORT)((base) + 4))
#define _LSR(base)          ((IO_PORT)((base) + 5))
#define _MSR(base)          ((IO_PORT)((base) + 6))
#define _SR(base)           ((IO_PORT)((base) + 7))

/* Interrupt Enable Register (IER) */
#define _ERXINT         0x01    /* Enable Received Data Available Interrupt */
#define _ETXEINT        0x02    /* Enable Transmitter Holding Register
                                   Empty Interrupt */
#define _ELSINT         0x04    /* Enable Receiver Line Status Interrupt */
#define _EMSINT         0x08    /* Enable Modem Status Interrupt */
#define _ESM            0x10    /* Enables Sleep Mode (16750) */
#define _ELPM           0x20    /* Enables Low Power Mode (16750) */

/* Interrupt Identification Register (IIR) */
#define _NIP            0x01    /* No Interrupt Pending */
#define _MSINT          0x00    /* Modem Status Interrupt */
#define _TXINT          0x02    /* Transmitter Holding Register Empty
                                   Interrupt */
#define _RXINT          0x04    /* Received Data Available Interrupt */
#define _LSINT          0x06    /* Receiver Line Status Interrupt */
#define _RXTINT         0x0c    /* 16550 Time-out Interrupt Pending */
#define _64FE           0x20    /* 64 Byte Fifo Enabled (16750 only) */
#define _NF             0x00    /* No FIFO */
#define _FEU            0x80    /* FIFO Enabled but Unusable */
#define _FE             0xc0    /* FIFO Enabled */

#define _INTBITMASK     0x0f    /* Interrupt Bit Mask */

/* First In / First Out Control Register (FCR) */
#define _EF             0x01    /* Enable FIFO's */
#define _CRXF           0x02    /* Clear Receive FIFO */
#define _CTXF           0x04    /* Clear Transmit FIFO */
#define _DMA            0x08    /* DMA Mode Select */
#define _E64F           0x20    /* Enable 64 Byte FIFO (16750 only) */
#define _ITL_1          0x00    /* Interrupt Trigger Level 1 Byte */
#define _ITL_4          0x40    /* Interrupt Trigger Level 4 Bytes */
#define _ITL_8          0x80    /* Interrupt Trigger Level 8 Bytes */
#define _ITL_14         0xc0    /* Interrupt Trigger Level 14 Bytes */

/* Line Control Register (LCR) */
#define _WL_5           0x00    /* Word Length 5 Bits */
#define _WL_6           0x01    /* Word Length 6 Bits */
#define _WL_7           0x02    /* Word Length 7 Bits */
#define _WL_8           0x03    /* Word Length 8 Bits */
#define _SB_1           0x00    /* 1 Stop Bit */
#define _SB_1_5         0x04    /* 1.5 Stop Bit */
#define _SB_2           _SB_1_5 /* 2 Stop Bit */
#define _NP             0x00    /* No Parity */
#define _OP             0x08    /* Odd Parity */
#define _EP             0x18    /* Even Parity */
#define _HP             0x28    /* High Parity (Sticky) */
#define _LP             0x38    /* Low Parity (Sticky) */
#define _SBE            0x40    /* Set Break Enable */
#define _DLAB           0x80    /* Divisor Latch Access Bit */

/* Modem Control Register (MCR) */
#define _DTR            0x01    /* Force Data Terminal Ready */
#define _RTS            0x02    /* Force Request to Send */
#define _AO1            0x04    /* Aux Output 1 */
#define _AO2            0x08    /* Aux Output 2 */
#define _LBM            0x10    /* LoopBack Mode */
#define _ACE            0x20    /* Autoflow Control Enabled (16750 only) */

/* Line Status Register (LSR) */
#define _DR             0x01    /* Data Ready */
#define _OERR           0x02    /* Overrun Error */
#define _PERR           0x04    /* Parity Error */
#define _FERR           0x08    /* Framing Error */
#define _BI             0x10    /* Break Interrupt */
#define _ETHR           0x20    /* Empty Transmitter Holding Register */
#define _EDHR           0x40    /* Empty Data Holding Registers */
#define _ERF            0x80    /* Error in Received FIFO */
#define _ERRMASK        0x1e    /* error mask */

/* Modem Status Register (MSR) */
#define _DCTS           0x01    /* Delta Clear to Send */
#define _DDSR           0x02    /* Delta Data Set Ready */
#define _TERI           0x04    /* Trailing Edge Ring Indicator */
#define _DDCR           0x08    /* Delta Data Carrier Detect */
#define _CTS            0x10    /* Clear To Send */
#define _DSR            0x20    /* Data Set Ready */
#define _RI             0x40    /* Ring Indicator */
#define _CD             0x80    /* Carrier Detect */
#define _MCSMASK        0xf0    /* Modem Current Status Mask */

/* Portable macro defines */
#define UART_MAX_BAUDRATE \
    SerialSettableBaudRate_256000

#define UART_AVL_BAUDRATES \
    (SerialSettableBaudRate_110     | \
     SerialSettableBaudRate_300     | \
     SerialSettableBaudRate_600     | \
     SerialSettableBaudRate_1200    | \
     SerialSettableBaudRate_2400    | \
     SerialSettableBaudRate_4800    | \
     SerialSettableBaudRate_9600    | \
     SerialSettableBaudRate_14400   | \
     SerialSettableBaudRate_19200   | \
     SerialSettableBaudRate_38400   | \
     SerialSettableBaudRate_56000   | \
     SerialSettableBaudRate_128000  | \
     SerialSettableBaudRate_115200  | \
     SerialSettableBaudRate_57600   | \
     SerialSettableBaudRate_256000)

#define UART_AVL_BYTESIZES \
    (SerialSettableByteSize_5 | \
     SerialSettableByteSize_6 | \
     SerialSettableByteSize_7 | \
     SerialSettableByteSize_8)

#define UART_AVL_STOPBITS_PARITY \
    (SerialSettableStopBits_One     | \
     SerialSettableStopBits_One5    | \
     SerialSettableStopBits_Two     | \
     SerialSettableParity_No        | \
     SerialSettableParity_Odd       | \
     SerialSettableParity_Even      | \
     SerialSettableParity_Mark      | \
     SerialSettableParity_Space)

#define UART_READ_RX_BUF(base, perrors) \
        Inb(_RXB(base))

#define UART_WRITE_TX_BUF(base, data) \
        Outb(_TXB(base), (data))

#define UART_DISABLE_INTS(base) \
        Outb(_IER(base), 0)
#define UART_ENABLE_RX_INTS(base) \
        Outb(_IER(base), _ERXINT | _ELSINT)
#define UART_ENABLE_RX_TX_INTS(base) \
        Outb(_IER(base), _ERXINT | _ETXEINT | _ELSINT)

#define UART_READ_INT_STAT(base) \
         (Inb(_IIR(base)) & _INTBITMASK)

#define UART_IS_RX_INT(base) \
         (((base) == _RXINT) || ((base) == _RXTINT))
#define UART_IS_TX_INT(base) \
         ((base) == _TXINT)
#define UART_IS_MS_INT(base) \
         ((base) == _MSINT)
#define UART_IS_LS_INT(base) \
         ((base) == _LSINT)

#define UART_CLR_RX_FIFO(base) \
        Outb(_FCR(base), _CRXF)

#define UART_CLR_TX_FIFO(base) \
        Outb(_FCR(base), _CTXF)

#define UART_SET_PARAMS(base, baudrate, parity, stopbits, bytesize) \
        UartSetParams((base), (baudrate), (parity), (stopbits), (bytesize))

#define UART_SET_BREAK(base) \
        Outb(_LCR(base), Inb(_LCR(base)) | _SBE)

#define UART_CLR_BREAK(base) \
        Outb(_LCR(base), Inb(_LCR(base)) & (~_SBE))

#define UART_READ_LS_ERR(base) \
        (Inb(_LSR(base)) & _ERRMASK)

#define UART_SUPPORT_MC(devno)  TRUE

#define UART_READ_MS(devno, base) \
        (Inb(_MSR(base)) & _MCSMASK)

/*
**  inline functions
*/
inline ECode UartSetParams(UInt32 uBaseAddr,
    SerialBaudRate baudRate, SerialParity parity,
    SerialStopBits stopBits, SerialByteSize byteSize)
{
    /* make the line parameter */
    Byte bLineParams = 0;

    switch (byteSize) {
        case SerialByteSize_5:      bLineParams |= _WL_5;   break;
        case SerialByteSize_6:      bLineParams |= _WL_6;   break;
        case SerialByteSize_7:      bLineParams |= _WL_7;   break;
        case SerialByteSize_8:      bLineParams |= _WL_8;   break;
        default: return E_INVALID_ARGUMENT;
    }

    switch (stopBits) {
        case SerialStopBits_One:    bLineParams |= _SB_1;   break;
        case SerialStopBits_One5:   bLineParams |= _SB_1_5; break;
        case SerialStopBits_Two:    bLineParams |= _SB_2;   break;
        default: return E_INVALID_ARGUMENT;
    }

    switch (parity) {
        case SerialParity_No:       bLineParams |= _NP;     break;
        case SerialParity_Odd:      bLineParams |= _OP;     break;
        case SerialParity_Even:     bLineParams |= _EP;     break;
        case SerialParity_Mark:     bLineParams |= _HP;     break;
        case SerialParity_Space:    bLineParams |= _LP;     break;
        default: return E_INVALID_ARGUMENT;
    }

    switch (baudRate) {
        case SerialBaudRate_110:
        case SerialBaudRate_300:
        case SerialBaudRate_600:
        case SerialBaudRate_1200:
        case SerialBaudRate_2400:
        case SerialBaudRate_4800:
        case SerialBaudRate_9600:
        case SerialBaudRate_14400:
        case SerialBaudRate_19200:
        case SerialBaudRate_38400:
        case SerialBaudRate_56000:
        case SerialBaudRate_57600:
        case SerialBaudRate_115200:
        case SerialBaudRate_128000:
        case SerialBaudRate_256000: break;
        default: return E_INVALID_ARGUMENT;
    }

    /* disable interrupt to set parameters */
    UART_DISABLE_INTS(uBaseAddr);

    /* set baud rate*/
    UInt16 u16Divisor = (UInt16)(115200 / baudRate);

    Outb(_LCR(uBaseAddr),
        (UInt8)(Inb(_LCR(uBaseAddr)) | _DLAB));
    Outb(_DLHB(uBaseAddr), (UInt8)((u16Divisor >> 8) & 0xff));
    Outb(_DLLB(uBaseAddr), (UInt8)(u16Divisor & 0xff));

    /* set line params */
    Outb(_LCR(uBaseAddr), bLineParams);

    /* enable fifo */
    Outb(_FCR(uBaseAddr), _EF | _CRXF | _CTXF | _ITL_1);

    /* clear modem control */
    Outb(_MCR(uBaseAddr), 0);

    /* delay and read */
    //_CThread_Delay(500 * 1000);
    UART_READ_RX_BUF(uBaseAddr, NULL);

    /* set modem control */
    Outb(_MCR(uBaseAddr), _DTR | _RTS | _AO2);

    /* reopen Rx interrupts */
    UART_ENABLE_RX_INTS(uBaseAddr);

    return NOERROR;
}

#endif /* __UART_H__ */
